package com.walker.taogong.datasource.postgresql;

import java.io.File;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.osgi.service.component.ComponentContext;
import org.osjava.sj.SimpleContext;
import org.osjava.sj.loader.JndiLoader;
import org.postgresql.ds.PGSimpleDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.walker.taogong.datasource.DataSourceProvider;
import com.walker.taogong.exception.TaoGongException;

/**
 * Provides a pooled datasource for PostgreSQL and binds it to SimpleJNDI.
 * 
 * @author HWellmann
 */
public class PostgresSimpleDataSourceProvider implements DataSourceProvider
{
    private static Logger log = LoggerFactory
            .getLogger(PostgresSimpleDataSourceProvider.class);

    private DataSource dataSource;

//    private AnacondaSettings config;
//
//    private AnacondaConfigManager cm;

    private void createDataSource()
    {
        PostgresSimpleDataSourceProvider.log
                .info("creating PostgreSQL DataSource");
//        JdbcSettings jdbc = this.cm.getConfiguration(null).getServer()
//                .getJdbc();

        /*
         * Simple DataSource which does not perform connection pooling. In order
         * to use the DataSource, you must set the property databaseName. The
         * settings for serverName, portNumber, user, and password are optional.
         * 
         * Note: these properties are declared in the superclass.
         */
        PGSimpleDataSource ds = new PGSimpleDataSource();
        ds.setServerName("localhost");
        ds.setDatabaseName("TaoGongDB");
        ds.setUser("anaconda");
        ds.setPassword("anaconda");
        this.dataSource = ds;

    }

    @Override
    public void registerDataSource()
    {
        PostgresSimpleDataSourceProvider.log
                .info("registering DataSource in JNDI");
//        this.config = this.cm.getConfiguration(null);
        this.createJndiContext();
        this.createDataSource();

        try
        {
            Context initialContext = new InitialContext();
            Context subcontext = initialContext
                    .createSubcontext(DataSourceProvider.TAOGONG_DATASOURCE_SUBCONTEXT);
            subcontext.bind(DataSourceProvider.TAOGONG_DATASOURCE_KEY,
                    this.dataSource);
        }
        catch (NamingException exc)
        {
            throw new TaoGongException(exc);
        }
    }

    protected void activate(ComponentContext cc)
    {
        PostgresSimpleDataSourceProvider.log
        .info("begining regist pooled PostgreSQL DataSource");
    }

    private void createJndiContext()
    {
        System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
                "org.osjava.sj.SimpleContextFactory");

        String jndiDir = this.createSimpleJndiDir();
        System.setProperty(SimpleContext.SIMPLE_ROOT, jndiDir);
        System.setProperty(JndiLoader.SIMPLE_DELIMITER, "/");

        // NOTE: There does not seem to be constant for this property.
        // JndiLoader.SIMPLE_SHARED expands to org.osjava.jndi.shared (no "sj")
        // and does not work. Is this a bug in SimpleJNDI?
        System.setProperty("org.osjava.sj.jndi.shared", "true");
    }

    /**
     * SimpleJNDI has uses a directory for storing properties. In some cases
     * which are hard to reconstruct, new InitialContext() throws an exception
     * when this directory is not empty.
     * <p>
     * So just to be on the safe side, we make sure that TMP_DIR/jndi is an
     * empty directory, where TMP_DIR is specified by the Anaconda
     * configuration.
     * 
     * @return working directory for SimpleJNDI
     */
    private String createSimpleJndiDir()
    {
        File tmpDir = new File("G:/TaoGong");
        if (!tmpDir.exists())
        {
            throw new TaoGongException(tmpDir.getPath() + " does not exist");
        }

        if (!tmpDir.isDirectory())
        {
            throw new TaoGongException(tmpDir + " must be a directory");
        }

        File jndiDir = new File(tmpDir, "jndi");
        if (!jndiDir.exists())
        {
            boolean success = jndiDir.mkdir();
            if (!success)
            {
                throw new TaoGongException("Cannot create " + jndiDir);
            }
        }
        else
        {
            if (!jndiDir.isDirectory())
            {
                throw new TaoGongException(jndiDir + " must be a directory");
            }

            if (jndiDir.list().length > 0)
            {
                throw new TaoGongException(jndiDir + " must be empty");
            }
        }
        return jndiDir.getPath();
    }

    protected void deactivate(ComponentContext cc)
    {
        this.unregisterDataSource();
        this.closeDataSource();
    }

    private void closeDataSource()
    {
        if (this.dataSource != null)
        {
            PostgresSimpleDataSourceProvider.log
                    .info("closing PostgreSQL DataSource");
            if (this.dataSource instanceof PGSimpleDataSource)
            {
                // no close method
                this.dataSource = null;
            }
        }
    }

    private void unregisterDataSource()
    {
//        if (this.config != null)
//        {
            PostgresSimpleDataSourceProvider.log
                    .info("unregistering DataSource in JNDI");
            try
            {
                Thread.currentThread().setContextClassLoader(
                        getClass().getClassLoader());
                Context initialContext = new InitialContext();
                initialContext
                        .unbind(DataSourceProvider.TAOGONG_DATASOURCE_JNDI_KEY);
            }
            catch (NamingException exc)
            {
                throw new TaoGongException(exc);
            }
//        }
    }

    @Override
    public DataSource getDataSource()
    {
        this.createDataSource();
        return this.dataSource;
    }

    @Override
    public void setChainedDataSource(DataSourceProvider chain)
    {
        return;
    }

    @Override
    public void closeChainedDataSource()
    {
        this.closeDataSource();
    }

    @Override
    public String[] getFlavors()
    {
        final String flavors[] = { "postgres" };
        return flavors;
    }
}
